`timescale 1ns/1ns
`include "DPRAM.v"
module ASFIFO #
(
  parameter WIDTH = 16, //数据总线宽度 
  parameter PTR  = 4   //fifo存储深度
)
(
  input wrclk,
  input rdclk,
  input wr_rst_n,
  input rd_rst_n,
  input wr_en,
  input rd_en,
  input [WIDTH-1:0]wr_data,
  output [WIDTH-1:0]rd_data,
  output reg wr_full,
  output reg rd_empty
);

//***********写时钟信号定义***************//
reg[PTR:0] wr_bin;
reg[PTR:0] wr_gray;
reg[PTR:0] rd_gray_ff1;
reg[PTR:0] rd_gray_ff2;
reg[PTR:0] rd_bin_wr;

//***********读时钟信号定义***************//
reg[PTR:0] rd_bin;
reg[PTR:0] rd_gray;
reg[PTR:0] wr_gray_ff1;
reg[PTR:0] wr_gray_ff2;
reg[PTR:0] wr_bin_rd;

integer i,j;

//**********DPRAM控制信号************//
wire dpram_wr_en;
wire [PTR-1:0] dpram_wr_addr;
wire [WIDTH-1:0] dpram_wr_data;
wire dpram_rd_en;
wire [PTR-1:0] dpram_rd_addr;
wire [WIDTH-1:0] dpram_rd_data;

//*********************写时钟域*******************//
//二进制写地址递增
always@(posedge wrclk or posedge wr_rst_n)begin
  if(!wr_rst_n)begin
    wr_bin <= 'b0;
  end  
  else if(wr_en == 1'b1 && wr_full == 1'b0)begin
    wr_bin <= wr_bin + 1'b1;
  end
  else begin
    wr_bin <= wr_bin;
  end
end
//*********二进制转换格雷码***********//
//********写地址**********************//

always@(posedge wrclk or posedge wr_rst_n)begin
  if(!wr_rst_n)begin
    wr_gray <= 'b0;
  end
  else begin
    wr_gray <= {wr_bin[PTR],wr_bin[PTR:1]^wr_bin[PTR-1:0]};
  end
end

//***********r2w********************//
//*************使用多个中间值打两拍gray>ff1>ff2最终两拍后取ff2***************//
always@(posedge wrclk or posedge wr_rst_n)begin
  if(!wr_rst_n)begin
    rd_gray_ff1 <= 'b0;
	rd_gray_ff2 <= 'b0;
  end
  else begin
    rd_gray_ff1 <= rd_gray;
	rd_gray_ff2 <= rd_gray_ff1;
  end
end
//**********rd_addr_bin 2 wr_addr_bin****************// 
always@(*)begin
  rd_bin_wr[PTR] = rd_gray_ff2[PTR];
  for(i=PTR-1;i>=0;i=i-1)
    rd_bin_wr[i] = rd_bin_wr[i+1]^rd_gray_ff2[i];
end

//**********写满*****************//

always@(*)begin
  if(wr_bin[PTR]!=rd_bin_wr[PTR]&&(wr_bin[PTR-1:0]==rd_bin_wr[PTR-1:0]))
    wr_full = 1'b1;
  else
    wr_full = 1'b0;
end

//************读时钟域********************//
always@(posedge rdclk or posedge rd_rst_n)begin
  if(!rd_rst_n)begin
    rd_bin <= 'b0;
  end
  else if(rd_en == 1'b1 && rd_empty == 1'b0)begin
    rd_bin <= rd_bin + 1'b1;
  end
  else begin
    rd_bin <= rd_bin;
  end
end
//***********读地址**************//

always@(posedge rdclk or posedge rd_rst_n)begin
  if(!rd_rst_n)begin
    rd_gray <= 'b0;
  end
  else begin
    rd_gray <= {rd_bin[PTR],rd_bin[PTR:1]^rd_bin[PTR-1:0]};
  end
end

always@(posedge rdclk or posedge rd_rst_n)begin
  if(!rd_rst_n)begin
    wr_gray_ff1 <= 'b0;
	wr_gray_ff2 <= 'b0;
  end
  else begin
    wr_gray_ff1 <= wr_gray;
	wr_gray_ff2 <= wr_gray_ff1;
  end
end

always@(*)begin
  wr_bin_rd[PTR] = wr_gray_ff2[PTR];
  for(j=PTR-1;j>=0;j=j-1)
    wr_bin_rd[j] = wr_bin_rd[j+1]^wr_gray_ff2[j];
end

always@(*)begin
  if(rd_bin == wr_bin_rd)
    rd_empty = 1'b1;
  else
    rd_empty = 1'b0;  
end

DPRAM
  #(.WIDTH(16),.DEPTH(16),.ADDR(4))
  U_DPRAM
  (
  .wrclk(wrclk),
  .rdclk(rdclk),
  .rd_rst_n(rd_rst_n),
  .wr_en(dpram_wr_en),
  .rd_en(dpram_rd_en),
  .wr_data(dpram_wr_data),
  .rd_data(dpram_rd_data),
  .wr_addr(dpram_wr_addr),
  .rd_addr(dpram_rd_addr)
  );
  
  assign dpram_wr_en = (wr_en == 1'b1 && wr_full == 1'b0)? 1'b1:1'b0;
  assign dpram_rd_en = (rd_en == 1'b1 && rd_empty == 1'b0)? 1'b1:1'b0;
  
  assign dpram_wr_data = wr_data;
  assign rd_data = dpram_rd_data;
  
  assign dpram_wr_addr = wr_bin[PTR-1:0];
  assign dpram_rd_addr = rd_bin[PTR-1:0];
endmodule